﻿using System;
using System.ComponentModel;
using System.Reflection;
using VMExplorer.Win32;
using VMExplorer.Forms;
using System.Globalization;

namespace VMExplorer.Dynamic
{
  public class FieldPropertyDescriptor : PropertyDescriptor
	// From http://msdn.microsoft.com/en-us/magazine/cc163816.aspx
  {
    #region Private fields

    private FieldInfo _field;

    #endregion
    #region Construction

    public FieldPropertyDescriptor (FieldInfo field)
			: base (field.Name, (Attribute [])field.GetCustomAttributes (typeof (Attribute), true))
		{
			_field = field;
    }

    #endregion
    #region Object overrides

    public override bool Equals (object obj)
		{
			FieldPropertyDescriptor other = obj as FieldPropertyDescriptor;
			return other != null && other._field.Equals (_field);
		}

		public override int GetHashCode () 
    { 
      return _field.GetHashCode (); 
    }

    #endregion
    #region PropertyDescriptor overrides

    public override bool IsReadOnly { get { return true; } }
		public override bool CanResetValue (object component) { return false; }
		public override void ResetValue (object component) { }

		public override bool ShouldSerializeValue (object component) 
		{
			// http://www.codeproject.com/KB/cpp/DynamicDropDownProperties.aspx
			//	When the PropertyGrid displays a value, it determines whether to show the value
			//	in bold by checking first for a DefaultValueAttribute.
			//	If no DefaultValueAttribute is found, then the PropertyGrid uses reflection
			//	to see if a method with the name ShouldSerializeValue exists.
			//	If the method does exist, the PropertyGrid calls it. 
			//	A return value of true produces bold text, false produces regular text.
			return false; 
		}

		public override Type ComponentType { get { return _field.DeclaringType; } }

		public override Type PropertyType 
    { 
      get 
      {
        Type type = _field.FieldType;
        bool isArray = type.IsArray;
        if (isArray) type = type.GetElementType();

        if (type == typeof (IntPtr))
          return isArray ? typeof (MemoryAddress[]) : typeof (MemoryAddress);

        if (type.DeclaringType == typeof (Native))
        {
          if (type == typeof (Native.UNICODE_STRING))
            return isArray ? typeof(string[]) : typeof (string);

          if (type == typeof (Native.HANDLE))
            return isArray ? typeof(string[]) : typeof (string);

          if(!type.IsEnum)
            return typeof(FieldsToProperties);
        }

        return _field.FieldType; 
      } 
    }

    delegate object ConversionDelegate(object a);
    class MyConverter<From, To>
    {
      public object Convert(object src, ConversionDelegate dlg)
      {
        if (src.GetType().IsArray)
        {
          From[] a = (From[])src;
          To[] b = new To[a.Length];
          for (int i=0; i < a.Length; i++)
          {
            b[i] = (To)dlg(a[i]);
          }
          return b;
        }
        else
        {
          return dlg((From)src);
        }
      }
    }

		public override object GetValue (object component)
		{
      object value = _field.GetValue (component);

      Type type = value.GetType ();
      bool isArray = type.IsArray;
      if (isArray) type = type.GetElementType();


      if (type == typeof(IntPtr))
      {
        return (new MyConverter<IntPtr, MemoryAddress>()).Convert(value, delegate(object o) 
        {
          IntPtr ip = (IntPtr)o;
          return new MemoryAddress(ip); 
        });
      }

      if (type.DeclaringType == typeof (Native))
      {
        if (type == typeof(Native.UNICODE_STRING))
        {
          return (new MyConverter<Native.UNICODE_STRING, string>()).Convert(value, delegate(object o)
          {
            Native.UNICODE_STRING us = (Native.UNICODE_STRING)o;
            return RemoteReader.ReadStringUni(MainForm.Doc.ProcessHandle, us.Buffer, us.Length);
          });
        }

        if (type == typeof(Native.HANDLE))
        {
          return (new MyConverter<Native.HANDLE, string>()).Convert(value, delegate(object o)
          {
            Native.HANDLE h = (Native.HANDLE)o;
            return "0x" + h.Handle.ToString("X8");
          });
        }

        if (!type.IsEnum)
        {
          Type converterType = typeof(MyConverter<,>).MakeGenericType(type, typeof(FieldsToProperties));
          ConversionDelegate dlg = delegate(object o) 
          { 
            return new FieldsToProperties(o); 
          };

          return converterType.InvokeMember(
            "Convert", 
            BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, 
            null, 
            Activator.CreateInstance(converterType), 
            new object[] {value, dlg} );

          //return new FieldsToProperties(value);
        }
      }

      return value;
		}

		public override void SetValue (object component, object value)
		{
      //READ-ONLY !
      //_field.SetValue (component, value);
      //OnValueChanged (component, EventArgs.Empty);
    }

    public override TypeConverter Converter 
    { 
      get 
      { 
        //if(!_field.FieldType.IsPrimitive && !_field.FieldType.IsEnum)
        //  return new FieldPropertyDescriptorConverter();
        return base.Converter;
      }
    }

    #endregion
    #region PropertyDescriptor

    public override bool IsLocalizable { get { return base.IsLocalizable; } }
    public override bool SupportsChangeEvents { get { return base.SupportsChangeEvents; } }

    public override void AddValueChanged (object component, EventHandler handler) { base.AddValueChanged (component, handler); }
    protected override void FillAttributes (System.Collections.IList attributeList) { base.FillAttributes (attributeList); }
    public override PropertyDescriptorCollection GetChildProperties (object instance, Attribute [] filter) { return base.GetChildProperties (instance, filter); }
    public override object GetEditor (Type editorBaseType) { return base.GetEditor(editorBaseType); }
    protected override object GetInvocationTarget (Type type, object instance) { return base.GetInvocationTarget (type, instance); }
    protected override void OnValueChanged (object component, EventArgs e) { base.OnValueChanged (component, e); }
    public override void RemoveValueChanged (object component, EventHandler handler) { base.RemoveValueChanged (component, handler); }

    #endregion
  }
}
